package com.tangguangdi.base.db.generator.service;

import com.tangguangdi.base.common.db.CodeType;
import com.tangguangdi.base.db.generator.entity.*;
import com.tangguangdi.base.db.properties.GeneratorProperties;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.io.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class GeneratorService {

    @Resource
    private DbService dbService;

    private Configuration cfg;

    public GeneratorService() {
        cfg = new Configuration(Configuration.VERSION_2_3_30);
        cfg.setClassForTemplateLoading(GeneratorService.class, "/");
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setWrapUncheckedExceptions(true);
        cfg.setFallbackOnNullLoopVariable(false);
    }

    public Map info() {
        Map map = new HashMap();
        map.put("dbConfig", dbService.generatorProperties());
        map.put("dbTables", tables());
        return map;
    }

    private List<TableView> tables() {
        try {
            List<TableView> views = new ArrayList<>();
            TablesQuery query = dbService.dbQuery().tablesQuery();
            List<Map> list = dbService.queryAndClose(dbService.connect(), query.getSql());
            for (Map map : list) {
                TableView view = new TableView();
                view.setComment(getMapVal(map, query.getComment()));
                view.setName(getMapVal(map, query.getName()));
                view.setEngine(getMapVal(map, query.getEngine()));
                view.setView(dbService.dbQuery().isView(map));
                views.add(view);
            }
            return views;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    private String getMapVal(Map map, String key) {
        Object o = map.get(key);
        if (o != null) return o.toString();
        return null;
    }

    public List<CodeView> view(GeneratorProperties properties) {
        List<CodeView> list = new ArrayList<>();
        CodeData codeData = getData(properties);
        if (properties.isEntity()) {
            list.add(codeGenerator(CodeType.ENTITY, codeData));
            list.add(codeGenerator(CodeType.XML, codeData));
        }
        if (properties.isService()) {
            list.add(codeGenerator(CodeType.MAPPER, codeData));
            list.add(codeGenerator(CodeType.SERVICE, codeData));
            list.add(codeGenerator(CodeType.IMPL, codeData));
        }
        if (properties.isApi()) {
            list.add(codeGenerator(CodeType.CONTROLLER, codeData));
        }
        if (properties.isPage()) {
            list.add(codeGenerator(CodeType.API, codeData));
            list.add(codeGenerator(CodeType.PAGE, codeData));
        }
        return list;
    }

    private CodeData getData(GeneratorProperties properties) {
        CodeData data = new CodeData();
        data.setProperties(properties);
        data.setCurrentDate(LocalDate.now().toString());
        data.setEntity(lineToHump(properties.getTableName(), properties.getModuleName()));
        data.setFields(getFields(properties.getTableName()));
        data.setFieldNames(getFieldNames(data.getFields()));
        data.setName((new StringBuilder())
                .append(Character
                        .toLowerCase(data.getEntity().charAt(0)))
                .append(data.getEntity()
                        .substring(1)).toString());
        data.setPackagePath(properties.getPackageName().replace(".", "/"));
        data.setViewOutputDir(properties.getViewOutputDir());
        data.setServerOutputDir(properties.getServerOutputDir());
        return data;
    }

    private String getFieldNames(List<TableField> fields) {
        String names = "";
        for (TableField field : fields) {
            names = names.concat(field.getName().concat(","));
        }
        return names.substring(0, names.length() - 1);
    }

    private List<TableField> getFields(String tableName) {
        try {
            List<TableField> fields = new ArrayList<>();
            FieldsQuery query = dbService.dbQuery().fieldsQuery();
            List<Map> list = dbService.queryAndClose(dbService.connect()
                    , String.format(query.getSql(), tableName));
            for (Map map : list) {
                TableField field = new TableField();
                field.setComment(map.get(query.getComment()).toString());
                field.setName(map.get(query.getName()).toString());
                field.setPropertyName(lineToHump(field.getName(), ""));
                field.setType(dbService.dbQuery().fieldType(map));
                field.setPrimaryKey(dbService.dbQuery().isPrimaryKey(map));
                fields.add(field);
            }
            return fields;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    private String lineToHump(String str, String prefix) {
        str = str.toLowerCase();
        Matcher matcher = Pattern.compile("_(\\w)").matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString().replace(prefix, "");
    }

    private CodeView codeGenerator(CodeType codeType, CodeData codeData) {
        CodeView codeView = new CodeView();
        codeView.setName(codeType.getName());
        codeView.setPath(codeType.getPath()
                .replace("{src}", System.getProperty("user.dir").replace("\\", "/"))
                .replace("{serverOutputDir}", codeData.getServerOutputDir())
                .replace("{viewOutputDir}", codeData.getViewOutputDir())
                .replace("{packagePath}", codeData.getPackagePath())
                .replace("{moduleName}", codeData.getProperties().getModuleName())
                .replace("{entity}", codeData.getEntity())
                .replace("{name}", codeData.getName()));
        try {
            Template temp = cfg.getTemplate("/templates/" + codeType.getTemplate());
            Writer writer = new StringWriter();
            temp.process(codeData, writer);
            codeView.setCode(writer.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return codeView;
    }


    public String template(String file) {
        return file;
    }

    public void generate(List<CodeView> views) {
        for (CodeView codeView : views){
            File codeFile = new File(codeView.getPath());
            if(!codeFile.getParentFile().exists()) codeFile.getParentFile().mkdirs();
            if(codeFile.exists()) codeFile.delete();
            try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(codeView.getPath()))) {
                bufferedWriter.write(codeView.getCode());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
